package lrscp.lib.tcp;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

import lrscp.lib.Common;
import lrscp.lib.Log;

public class TcpStringCommunicate {
    private static final boolean DEBUG = false;
    Object mUpDownLock = new Object();
    BufferedWriter bw = null;
    BufferedReader br = null;

    TcpSocket ts = null;

    private RecvListener mLsnr = null;

    private boolean exit = false;
    private boolean inited = false;

    public static interface RecvListener {
        public void onReceive(String data);
    }

    private TcpStringCommunicate(String address, int port) {
        ts = new TcpClient(address, port) {
            @Override
            public void handleStream(InputStream is, OutputStream os) throws IOException {
                TcpStringCommunicate.this.handleStream(is, os);
            }
        };
    }

    private TcpStringCommunicate(int port) {
        ts = new TcpServer(port) {
            @Override
            public void handleStream(InputStream is, OutputStream os) throws IOException {
                TcpStringCommunicate.this.handleStream(is, os);
            }
        };
    }

    public static TcpStringCommunicate createServer(int port) {
        TcpStringCommunicate tsc = new TcpStringCommunicate(port);
        return tsc;
    }

    public static TcpStringCommunicate createClient(String address, int port) {
        TcpStringCommunicate tsc = new TcpStringCommunicate(address, port);
        return tsc;
    }

    private void handleStream(InputStream is, OutputStream os) throws IOException {
        br = new BufferedReader(new InputStreamReader(is));
        bw = new BufferedWriter(new OutputStreamWriter(os));
        inited = true;
        while (!exit) {
            boolean confirm = false;
            String data = null;
            synchronized (mUpDownLock) {
                if (br.ready()) {
                    data = br.readLine();
                    confirm = confirm(data);
                }
            }
            if (confirm && mLsnr != null)
                mLsnr.onReceive(data);
            Common.sleep(10);
        }
    }

    public void setRecvListener(RecvListener lsnr) {
        mLsnr = lsnr;
    }

    public void send(String content) {
        try {
            waitForInit();
            synchronized (mUpDownLock) {
                while (!exit) {
                    clear(br);
                    if (DEBUG)
                        Log.i("server", "send :" + content);
                    bw.append(content + "\n");
                    bw.flush();
                    String resp = br.readLine();
                    if (resp.equals(content)) {
                        bw.append("ok\n");
                        bw.flush();
                        if (DEBUG)
                            Log.i("server", "send sucess:" + content);
                        return;
                    } else {
                        if (DEBUG)
                            Log.e("server", "error response:" + resp + ", retry");
                    }
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean confirm(String content) throws IOException {
        clear(br);
        bw.append(content + "\n");
        bw.flush();
        String resp = br.readLine();
        if (resp.equals("ok")) {
            if (DEBUG)
                Log.i("client", "confirm sucess:" + content);
            return true;
        }
        Log.e("client", "confirm fail:" + content + " resp:" + resp);
        return false;
    }

    private void clear(BufferedReader br) throws IOException {
        while (br.ready())
            br.read();
    }

    public void dispose() throws Exception {
        exit = true;
        ts.dispose();
    }

    public void asynDispose() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    dispose();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private void waitForInit() {
        while (!inited && !exit)
            Common.sleep(100);
    }
}
